#include <framework/framework_i.h>

#define ENTRY_MOD_NAME "__module_main"

/* For the naive mapping strategy */
#define PADDR(kva) ((physaddr_t)(kva) - KBASE)
#define KADDR(pa)  (void *)((pa) + KBASE)

pdir_entry_t boot_ap_vpd[PAGEDIR_COUNT] __attribute__((aligned(PAGE_SIZE)));

         struct lcpu_static_s  lcpu_static[LAPIC_COUNT];
volatile struct lcpu_dynamic_s lcpu_dynamic[LAPIC_COUNT];

volatile struct boot_ap_info_s boot_ap_info;

extern pdir_entry_t init_vpd[PAGEDIR_COUNT];

static physaddr_t            cpu_vpt[LAPIC_COUNT];
static struct elf_mod_info_s cpu_mod_info[LAPIC_COUNT];
static void                 *cpu_entry[LAPIC_COUNT];

int
lcpu_init(void)
{
	 memset(cpu_vpt, 0, sizeof(cpu_vpt));
	 
	 memmove(KADDR(BOOT_AP_ENTRY), boot_ap_entry, PAGE_SIZE);
	 memmove(KADDR(BOOT_AP_GDT), boot_ap_gdt, PAGE_SIZE);
	 memmove(KADDR(BOOT_AP_GDT_DESC), boot_ap_gdt_desc, PAGE_SIZE);
	 memmove(boot_ap_vpd, init_vpd, PAGE_SIZE);
	 boot_ap_vpd[0] = boot_ap_vpd[PAGEDIR_IDX(KBASE)];
	 int i;
	 for (i = 0; i != ekf_sysconf.lcpu_count; ++ i)
	 {
		  int cpu = apic_map[i];
		  cpu_vpt[cpu] = vmem_vpt_new(1);
		  
		  if (cpu != ekf_sysconf.lcpu_boot)
		  {
			   boot_ap_info.id = cpu;
			   boot_ap_info.vpt = cpu_vpt[cpu];
			   boot_ap_info.stack_top = lcpu_static[cpu].initial_stack_top;
			   
			   spl_init(&boot_ap_info.success_lock);
			   spl_acquire(&boot_ap_info.success_lock);
			   
			   spl_init(&lcpu_dynamic[cpu].start_lock);
			   spl_acquire(&lcpu_dynamic[cpu].start_lock);

			   lapic_startap(cpu, BOOT_AP_ENTRY);
			   
			   spl_acquire(&boot_ap_info.success_lock);
			   spl_release(&boot_ap_info.success_lock);
		  }
		  else
		  {
			   lcr3(cpu_vpt[cpu]);
			   /* Initialize for bp */
			   mm_init();
			   elf_mod_load_by_name(ENTRY_MOD_NAME,
									&cpu_mod_info[cpu],
									&cpu_entry[cpu]);
		  }
	 }

	 for (i = 0; i != ekf_sysconf.lcpu_count; ++ i)
	 {
		  int cpu = apic_map[i];
		  lcpu_static[cpu].entry = cpu_entry[cpu];
	 }

	 /* AFTER FIX =_= */
	 vmem_init_cpupriv();
	 return 0;
}

static void
boot_ap_main(void)
{
	 kprintf("Starting ap %d ...\n", lcpu_id);
	 kprintf("lapic...");
	 lapic_init_ap();
	 kprintf("vmem...");
	 vmem_init_ap(boot_ap_info.vpt);
	 vmem_init_cpupriv();
	 kprintf("intr...");
	 intr_init_ap();
	 kprintf("task...");
	 task_init_ap();
	 kprintf("done\n");

	 mm_init();
	 elf_mod_load_by_name(ENTRY_MOD_NAME,
						  &cpu_mod_info[lcpu_id],
						  &cpu_entry[lcpu_id]);

	 spl_release(&boot_ap_info.success_lock);
	 spl_acquire(&lcpu_dynamic[lcpu_id].start_lock);

	 kprintf("LCPU %d Start!\n", lcpu_id);
	 lcpu_static[lcpu_id].entry();
	 kprintf("LCPU %d Stop!\n", lcpu_id);
	 while (1) ;
}

void
boot_ap_c_entry(void)
{
	 __asm __volatile("movl %0, %%esp" ::
	 				  /* -16 for safty, check the asm of the function */
	 				  "r" (boot_ap_info.stack_top));
	 boot_ap_main();
}

int
lcpu_set_vpt_delay(int lcpu, physaddr_t vpt)
{
	 lcpu_dynamic[lcpu].vpt_delay = vpt;
}
